home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus Extra 1997 #1 / Amiga Plus Extra 1997 #1.iso / programme / tools / leoutils / guidecat.c < prev    next >
C/C++ Source or Header  |  1996-11-25  |  28KB  |  1,048 lines

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include "LeoLib.h"
  5.  
  6. #define VERSION "1.11"
  7.  
  8. #if !defined _AMIGA && !defined AMIGA
  9. typedef short BOOL;
  10. #endif
  11. #ifndef TRUE
  12. #define TRUE 1
  13. #endif
  14. #ifndef FALSE
  15. #define FALSE 0
  16. #endif
  17.  
  18. /* The following are for ANSI C compilers with bad stdio.h */
  19. #if !defined _IOFBF
  20. #define _IOFBF 0
  21. #endif
  22.  
  23. static void SetStupidTerminal(BOOL Stupid);
  24. static void Bold(FILE *OutFile);
  25. static void Italics(FILE *OutFile);
  26. static void Reverse(FILE *OutFile);
  27. static void Underline(FILE *OutFile);
  28. static void BoldOff(FILE *OutFile);
  29. static void ItalicsOff(FILE *OutFile);
  30. static void ReverseOff(FILE *OutFile);
  31. static void UnderlineOff(FILE *OutFile);
  32. static void FGColor(FILE *OutFile, int Color);
  33. static void BGColor(FILE *OutFile, int Color);
  34. static void Normal(FILE *OutFile);
  35. static void StoreTextMode(void);
  36. static void RestoreTextMode(FILE *OutFile);
  37.  
  38. static void ReadAGFile(FILE *InFile);
  39. static void WordAddChar(char NewC);
  40. static void WordSend(void);
  41.  
  42. static void SkipSpaces(FILE *InFile);
  43. static void SkipUntilSpace(FILE *InFile);
  44. static void SkipToEOL(FILE *InFile);
  45. static int ReadToUpper(FILE *InFile, char *string, int n);
  46.  
  47. static void PutInit(FILE *OutFile);
  48. static void PutWord(char *Str);
  49. static void PutEOL(void);
  50. static void PutEnd(void);
  51. static void PutTab(void);
  52. static void PutSpace(void);
  53. static void PutSetWidth(int Width);
  54. static void PutSetWrap(void);
  55. static void PutPurgeSpace(void);
  56.  
  57. static void NodeInit(void);
  58. static int NodeNumber(char *Str);
  59. static void NodeEnd(void);
  60.  
  61. static int AGParseCommand(char *Command, FILE *InFile);
  62. static int AGArgs(void);
  63. static char *AGArg(int Arg);
  64. static char *AGArgU(int Arg);
  65.  
  66.  
  67. #if defined _AMIGA || defined AMIGA
  68. static char *Ver = "$VER:GuideCat " VERSION
  69. #if defined __SASC
  70.     " " __AMIGADATE__ " ©1994-6 Leopold-Soft"
  71. #endif
  72. ;
  73. #endif
  74.  
  75. static BOOL NodeItalics = TRUE,  NodeBold = TRUE,  NodeReverse = TRUE,
  76.     LinkItalics = FALSE, LinkBold = FALSE, LinkReverse = TRUE,
  77.     IndexNumbering = TRUE, fileArguments = FALSE, Plain = FALSE,
  78.     Brackets = FALSE;
  79. static int NodeColor = 1, LinkColor = 1, ColColor = 2;
  80. static BOOL ColReverse = FALSE;
  81. static void *InBuff = NULL;
  82.  
  83.  
  84.  
  85.  
  86. /***********************************\
  87. *                                    *
  88. * Main - command line parser        *
  89. *                                    *
  90. \***********************************/
  91.  
  92. int main(int argc, char **argv) {
  93.     static int CurrArg = 0;
  94.     static FILE *InFile;
  95.     static char *ColumnsStr;
  96.     static int Columns = 0;
  97.  
  98.     if ((ColumnsStr = getenv("COLUMNS")) && (Columns = atoi(ColumnsStr)))
  99.         PutSetWidth(Columns);
  100.  
  101.     while(++CurrArg < argc) {
  102.                                 /* NODE OPTIONS */
  103.         if (!strcmp(argv[CurrArg],"-nI")) {
  104.             NodeItalics = TRUE;
  105.             Plain = FALSE;
  106.         } else if (!strcmp(argv[CurrArg],"-ni")) {
  107.             NodeItalics = FALSE;
  108.             Plain = FALSE;
  109.         } else if (!strcmp(argv[CurrArg],"-nB")) {
  110.             NodeBold = TRUE;
  111.             Plain = FALSE;
  112.         } else if (!strcmp(argv[CurrArg],"-nb")) {
  113.             NodeBold = FALSE;
  114.             Plain = FALSE;
  115.         } else if (!strcmp(argv[CurrArg],"-nR")) {
  116.             NodeReverse = TRUE;
  117.             Plain = FALSE;
  118.         } else if (!strcmp(argv[CurrArg],"-nr")) {
  119.             NodeReverse = FALSE;
  120.             Plain = FALSE;
  121.         } else if (!strcmp(argv[CurrArg],"-nC")) {
  122.             if (argc > CurrArg+1) {
  123.                 NodeColor = atoi(argv[++CurrArg]);
  124.                 if (NodeColor < 1 || NodeColor > 7) {
  125.                     NodeColor = 1;
  126.                     CurrArg--;
  127.                 }
  128.             }
  129.             Plain = FALSE;
  130.         } else if (!strcmp(argv[CurrArg],"-nc")) {
  131.             NodeColor = 1;
  132.             Plain = FALSE;
  133.                                 /* LINK OPTIONS */
  134.         } else if (!strcmp(argv[CurrArg],"-lI")) {
  135.             LinkItalics = TRUE;
  136.             Plain = FALSE;
  137.         } else if (!strcmp(argv[CurrArg],"-li")) {
  138.             LinkItalics = FALSE;
  139.             Plain = FALSE;
  140.         } else if (!strcmp(argv[CurrArg],"-lB")) {
  141.             LinkBold = TRUE;
  142.             Plain = FALSE;
  143.         } else if (!strcmp(argv[CurrArg],"-lb")) {
  144.             LinkBold = FALSE;
  145.             Plain = FALSE;
  146.         } else if (!strcmp(argv[CurrArg],"-lR")) {
  147.             LinkReverse = TRUE;
  148.             Plain = FALSE;
  149.         } else if (!strcmp(argv[CurrArg],"-lr")) {
  150.             LinkReverse = FALSE;
  151.             Plain = FALSE;
  152.         } else if (!strcmp(argv[CurrArg],"-lC")) {
  153.             if (argc > CurrArg+1) {
  154.                 LinkColor = atoi(argv[++CurrArg]);
  155.                 if (LinkColor < 1 || LinkColor > 7) {
  156.                     LinkColor = 1;
  157.                     CurrArg--;
  158.                 }
  159.             }
  160.             Plain = FALSE;
  161.         } else if (!strcmp(argv[CurrArg],"-lc")) {
  162.             LinkColor = 1;
  163.             Plain = FALSE;
  164.                                 /* COLOR OPTIONS */
  165.         } else if (!strcmp(argv[CurrArg],"-cR")) {
  166.             ColReverse = TRUE;
  167.             ColColor = 1;
  168.             Plain = FALSE;
  169.         } else if (!strcmp(argv[CurrArg],"-cr")) {
  170.             ColReverse = FALSE;
  171.             Plain = FALSE;
  172.         } else if (!strcmp(argv[CurrArg],"-cC")) {
  173.             if (argc > CurrArg+1) {
  174.                 ColColor = atoi(argv[++CurrArg]);
  175.                 if (ColColor < 1 || ColColor > 7) {
  176.                     ColColor = 1;
  177.                     CurrArg--;
  178.                 }
  179.             }
  180.             ColReverse = FALSE;
  181.             Plain = FALSE;
  182.         } else if (!strcmp(argv[CurrArg],"-cc")) {
  183.             ColColor = 1;
  184.                                 /* OTHER OPTIONS */
  185.         } else if (!strcmp(argv[CurrArg],"-I")) {
  186.             IndexNumbering = TRUE;
  187.         } else if (!strcmp(argv[CurrArg],"-i")) {
  188.             IndexNumbering = FALSE;
  189.         } else if (!strcmp(argv[CurrArg],"-S")) {
  190.             SetStupidTerminal(TRUE);
  191.         } else if (!strcmp(argv[CurrArg],"-s")) {
  192.             SetStupidTerminal(FALSE);
  193.         } else if (!strcmp(argv[CurrArg],"-B")) {
  194.             Brackets = TRUE;
  195.         } else if (!strcmp(argv[CurrArg],"-b")) {
  196.             Brackets = FALSE;
  197.         } else if (!strcmp(argv[CurrArg],"-P")) {
  198.             Plain = TRUE;
  199.         } else if (!strcmp(argv[CurrArg],"-p")) {
  200.             Plain = FALSE;
  201.         } else if (!strcmp(argv[CurrArg],"-A")) {
  202.             Brackets = TRUE;
  203.         } else if (!strcmp(argv[CurrArg],"-a")) {
  204.             Brackets = FALSE;
  205.         } else if (!strcmp(argv[CurrArg],"-w") || !strcmp(argv[CurrArg],"-W")) {
  206.             if (argc > CurrArg+1) {
  207.                 static int Tmp;
  208.                 Tmp = atoi(argv[++CurrArg]);
  209.                 if (Tmp == 0) {
  210.                     CurrArg--;
  211.                 } else {
  212.                     PutSetWidth(Tmp);
  213.                 }
  214.             }
  215.         } else if (!strcmp(argv[CurrArg],"-d0")) {
  216.             NodeItalics = TRUE;  NodeBold = TRUE;  NodeReverse = TRUE;
  217.             LinkItalics = FALSE; LinkBold = FALSE; LinkReverse = TRUE;
  218.             IndexNumbering = TRUE; fileArguments = FALSE; Plain = FALSE;
  219.             Brackets = FALSE; NodeColor = 1; LinkColor = 1;
  220.             ColReverse = FALSE; ColColor = 2;
  221.             SetStupidTerminal(TRUE);
  222.         } else if (!strcmp(argv[CurrArg],"-d1")) {
  223.             NodeItalics = FALSE;  NodeBold = TRUE;  NodeReverse = FALSE;
  224.             LinkItalics = FALSE; LinkBold = TRUE; LinkReverse = FALSE;
  225.             IndexNumbering = TRUE; fileArguments = FALSE; Plain = FALSE;
  226.             Brackets = FALSE; NodeColor = 3; LinkColor = 3;
  227.             ColReverse = FALSE; ColColor = 2;
  228.             SetStupidTerminal(FALSE);
  229.         } else if (!strcmp(argv[CurrArg],"-d2")) {
  230.             NodeItalics = FALSE; NodeBold = FALSE; NodeReverse = FALSE;
  231.             LinkItalics = FALSE; LinkBold = FALSE; LinkReverse = FALSE;
  232.             IndexNumbering = TRUE; fileArguments = FALSE; Plain = TRUE;
  233.             Brackets = TRUE; NodeColor = 1; LinkColor = 1;
  234.             ColReverse = FALSE; ColColor = 1;
  235.             SetStupidTerminal(TRUE);
  236.         } else if (!strcmp(argv[CurrArg],"-h") || !strcmp(argv[CurrArg],"?")) {
  237.             printf("\nGuideCat " VERSION " ©1994-6 Henrik Herranen\n\n"
  238.                 "Usage: %s [?|-h] | [-n[I|i]] | [-n[B|b]] | [-n[R|r]] | [-l[I|i]] | [-l[B|b]] | [-l[R|r]] | [filename]\n\n"
  239.                 "-h or ?      Show this help page.\n"
  240.                 "                                             def: 0   1   2\n"
  241.                 "-nI or -ni   Turns Italics on/off in node headers on  off off\n"
  242.                 "-nB or -nb   Turns Bold on/off in node headers    on  off off\n"
  243.                 "-nR or -nr   Turns Reverse on/off in node headers on  off off\n"
  244.                 "-nC x or -nc Sets color on/off in node headers    off 3   off\n"
  245.                 "-lI or -li   Turns Italics on/off in links        off off off\n"
  246.                 "-lB or -lb   Turns Bold on/off in links           off off off\n"
  247.                 "-lR or -lr   Turns Reverse on/off in links        on  off off\n"
  248.                 "-lC x or -lc Sets color on/off in links           off 3   off\n"
  249.                 "-cR or -cr   Turns Reverse on/off for colors      off off off\n"
  250.                 "-cC x or -cc Sets colors on+hilite/off for colors 2   2   off\n"
  251.                 "-I or -i     Switches node index numbering on/off on  on  on\n"
  252.                 "-S or -s     Switches stupid terminal mode on/off on  off on\n"
  253.                 "-A or -a     Brackets around nodes+links on/off   off off on\n"
  254.                 "-P pr -p     plain output on/off                  off off on\n"
  255.                 "-w x (x>=20) Set display width if WORDWRAP used   78  78  78\n"
  256.                 "-d0 .. -d2   Use defaults 0..2\n"
  257.                 "filename     the named file will be read as an AmigaGuide file.\n\n"
  258.                 ,argv[0]);
  259.             fileArguments = TRUE;
  260.         } else {
  261.             if (InFile = fopen(argv[CurrArg], "ra")) {
  262.                 if (!InBuff) InBuff = malloc(16384);
  263.                 if (InBuff) setvbuf(InFile, InBuff, _IOFBF, (size_t) 16384);
  264.                 ReadAGFile(InFile);
  265.                 fclose(InFile);
  266.             } else {
  267.                 fprintf(stderr,"%s: *** ERROR: Couldn't open file '%s'!\n", argv[0], argv[CurrArg]);
  268.             }
  269.             fileArguments = TRUE;
  270.         }
  271.     }
  272.  
  273.     if (!fileArguments) {
  274.         fprintf(stderr,"%s: Reading from standard input. Press "
  275. #if defined _AMIGA || defined AMIGA
  276.                 "ctrl-\\"
  277. #else
  278.                 "ctrl-D"
  279. #endif
  280.                 " to stop if running interactively.\n", argv[0]);
  281.         ReadAGFile(stdin);
  282.     }
  283.  
  284.     return EXIT_SUCCESS;
  285. }
  286.  
  287.  
  288.  
  289. /***********************************\
  290. *                                    *
  291. * Text formatting functions            *
  292. *                                    *
  293. \***********************************/
  294.  
  295. struct TextMode {
  296.     BOOL Bold, Italics, Reverse, Underline;
  297.     int FGColor, BGColor;
  298. };
  299.  
  300. static struct TextMode CurrTextMode = {FALSE, FALSE, FALSE, FALSE, 1, 0}, StoredTextMode;
  301. static BOOL StupidTerminal = TRUE;
  302.  
  303. static void SetStupidTerminal(BOOL Stupid) {
  304.     StupidTerminal = Stupid;
  305. }
  306.  
  307. static void Bold(FILE *OutFile) {
  308.     if (!CurrTextMode.Bold) {
  309.         fprintf(OutFile,"\033[1m");
  310.         CurrTextMode.Bold = TRUE;
  311.     }
  312. }
  313.  
  314. static void Italics(FILE *OutFile) {
  315.     if (!CurrTextMode.Italics) {
  316.         fprintf(OutFile,"\033[3m");
  317.         CurrTextMode.Italics = TRUE;
  318.     }
  319. }
  320.  
  321. static void Reverse(FILE *OutFile) {
  322.     if (!CurrTextMode.Reverse) {
  323.         fprintf(OutFile,"\033[7m");
  324.         CurrTextMode.Reverse = TRUE;
  325.     }
  326. }
  327.  
  328. static void Underline(FILE *OutFile) {
  329.     if (!CurrTextMode.Underline) {
  330.         fprintf(OutFile,"\033[4m");
  331.         CurrTextMode.Underline = TRUE;
  332.     }
  333. }
  334.  
  335. /* The following function is only for internal use    */
  336. /*        -> no prototype provided                    */
  337. static void SetTextModeOnStupidTerminal(FILE *OutFile) {
  338.     struct TextMode TmpTextMode = CurrTextMode;
  339.     Normal(OutFile);
  340.     if (TmpTextMode.Bold) Bold(OutFile);
  341.     if (TmpTextMode.Italics) Italics(OutFile);
  342.     if (TmpTextMode.Reverse) Reverse(OutFile);
  343.     if (TmpTextMode.Underline) Underline(OutFile);
  344.     if (TmpTextMode.FGColor != 1) FGColor(OutFile, StoredTextMode.FGColor);
  345.     if (TmpTextMode.BGColor != 0) BGColor(OutFile, StoredTextMode.BGColor);
  346. }
  347.  
  348. static void BoldOff(FILE *OutFile) {
  349.     if (CurrTextMode.Bold) {
  350.         CurrTextMode.Bold = FALSE;
  351.         if (StupidTerminal) SetTextModeOnStupidTerminal(OutFile);
  352.         else fprintf(OutFile,"\033[22m");
  353.     }
  354. }
  355.  
  356. static void ItalicsOff(FILE *OutFile) {
  357.     if (CurrTextMode.Italics) {
  358.         CurrTextMode.Italics = FALSE;
  359.         if (StupidTerminal) SetTextModeOnStupidTerminal(OutFile);
  360.         else fprintf(OutFile,"\033[23m");
  361.     }
  362. }
  363.  
  364. static void ReverseOff(FILE *OutFile) {
  365.     if (CurrTextMode.Reverse) {
  366.         CurrTextMode.Reverse = FALSE;
  367.         if (StupidTerminal) SetTextModeOnStupidTerminal(OutFile);
  368.         else fprintf(OutFile,"\033[27m");
  369.     }
  370. }
  371.  
  372. static void UnderlineOff(FILE *OutFile) {
  373.     if (CurrTextMode.Underline) {
  374.         CurrTextMode.Underline = FALSE;
  375.         if (StupidTerminal) SetTextModeOnStupidTerminal(OutFile);
  376.         else fprintf(OutFile,"\033[24m");
  377.     }
  378. }
  379.  
  380. static void FGColor(FILE *OutFile, int Color) {
  381.     fprintf(OutFile,"\033[3%dm", Color & 7);
  382.     CurrTextMode.FGColor = Color & 7;
  383. }
  384.  
  385. static void BGColor(FILE *OutFile, int Color) {
  386.     fprintf(OutFile,"\033[4%dm", Color & 7);
  387.     CurrTextMode.BGColor = Color & 7;
  388. }
  389.  
  390. static void Normal(FILE *OutFile) {
  391.     CurrTextMode.Bold = FALSE;
  392.     CurrTextMode.Italics = FALSE;
  393.     CurrTextMode.Reverse = FALSE;
  394.     CurrTextMode.Underline = FALSE;
  395.     CurrTextMode.FGColor = 1;
  396.     CurrTextMode.BGColor = 0;
  397.     fprintf(OutFile,"\033[0m");
  398. }
  399.  
  400. static void StoreTextMode(void) {
  401.     StoredTextMode = CurrTextMode;
  402. }
  403.  
  404. static void RestoreTextMode(FILE *OutFile) {
  405.     if (CurrTextMode.Bold != StoredTextMode.Bold ||
  406.                 CurrTextMode.Italics != StoredTextMode.Italics ||
  407.                 CurrTextMode.Reverse != StoredTextMode.Reverse ||
  408.                 CurrTextMode.Underline != StoredTextMode.Underline ||
  409.                 CurrTextMode.FGColor != StoredTextMode.FGColor ||
  410.                 CurrTextMode.BGColor != StoredTextMode.BGColor) {
  411.         if (CurrTextMode.Bold || CurrTextMode.Italics ||
  412.             CurrTextMode.Reverse || CurrTextMode.Underline ||
  413.             CurrTextMode.FGColor != 1 || CurrTextMode.BGColor != 0) {
  414.             Normal(OutFile);
  415.             if (StoredTextMode.Bold) Bold(OutFile);
  416.             if (StoredTextMode.Italics) Italics(OutFile);
  417.             if (StoredTextMode.Reverse) Reverse(OutFile);
  418.             if (StoredTextMode.Underline) Underline(OutFile);
  419.             if (StoredTextMode.FGColor != 1) FGColor(OutFile, StoredTextMode.FGColor);
  420.             if (StoredTextMode.BGColor != 0) BGColor(OutFile, StoredTextMode.BGColor);
  421.         }
  422.     }
  423. }
  424.  
  425.  
  426.  
  427.  
  428. static int ReadToUpper(FILE *InFile, char *string, int n) {
  429.     static int i, InC;
  430.  
  431.     for (i=0; i<n; i++) {
  432.         if ((InC = fgetc(InFile)) != -1) {
  433.             *(string++) = ToUpper(InC);
  434.         } else {
  435.             return i;
  436.         }
  437.     }
  438.     return n;
  439. }
  440.  
  441. static void SkipSpaces(FILE *InFile) {
  442.     static int InC;
  443.     while (IsSpace(InC = fgetc(InFile)));
  444.     if (InC != -1) ungetc(InC, InFile);
  445. }
  446.  
  447. static void SkipUntilSpace(FILE *InFile) {
  448.     static int InC;
  449.     while (!IsSpace(InC = fgetc(InFile)) && InC != -1);
  450.     if (InC != -1) ungetc(InC, InFile);
  451. }
  452.  
  453. static void SkipToEOL(FILE *InFile) {
  454.     static int InC;
  455.     while ((InC = fgetc(InFile)) != -1 && InC != '\n');
  456.     if (InC != -1) ungetc(InC, InFile);
  457. }
  458.  
  459.  
  460.  
  461.  
  462. /***********************************\
  463. *                                    *
  464. * The AmigaGuide parser                *
  465. *                                    *
  466. \***********************************/
  467.  
  468. static void ReadAGFile(FILE *InFile) {
  469.     static char TmpStr[81], TmpStr2[81];
  470.     static int InC, i;
  471.     BOOL LastNewLine = TRUE, InsideNode = FALSE, Command = FALSE;
  472.  
  473.     ReadToUpper(InFile,TmpStr,10);
  474.     if (memcmp(TmpStr,"@DATABASE ",10)) {
  475.         fprintf(stderr,"   *** ERROR: Not a valid AmigaGuide file!\n");
  476.         return;
  477.     }
  478.  
  479.     printf("\nDATABASE: ");
  480.     while ((InC = fgetc(InFile)) != '\n') {
  481.         if (InC == -1) {
  482.             fprintf(stderr,"   *** ERROR: Unexpected end of file!\n");
  483.             return;
  484.         } else {
  485.             fputc(InC, stdout);
  486.         }
  487.     }
  488.     fputc('\n', stdout);
  489.  
  490.     PutInit(stdout);
  491.     NodeInit();
  492.  
  493.     while (!feof(InFile)) {
  494.         InC = fgetc(InFile);
  495.         if (InC == '@') {            /* The command character @ */
  496.             InC = fgetc(InFile);
  497.             if (LastNewLine && InC != '{') {            /* If a command line */
  498.                 ungetc(InC, InFile);
  499.                 AGParseCommand(NULL, InFile);
  500.                 if (feof(InFile)) return;
  501.                 if (!strcmp(AGArgU(0), "NODE")) {    /* recognize commands */
  502.                     if (!InsideNode) {
  503.                         InsideNode = TRUE;
  504.                         Normal(stdout);
  505.                         printf("\n\n\n%sNODE: ", (Brackets ? "[" : ""));
  506.                         if (!Plain) {
  507.                             if (NodeItalics) Italics(stdout);
  508.                             if (NodeBold) Bold(stdout);
  509.                             if (NodeReverse) Reverse(stdout);
  510.                             if (NodeColor != 1) FGColor(stdout, NodeColor);
  511.                         }
  512.                         if (IndexNumbering) printf("(%d) ", NodeNumber(AGArgU(1)));
  513.                         if (AGArgs() > 2) {
  514.                             printf("%s", AGArg(2));
  515.                         } else {
  516.                             printf("*** Internal name: %s ***", AGArgU(1));
  517.                         }
  518.                         if ((NodeItalics || NodeBold || NodeReverse || NodeColor != 1)
  519.                                     && !Plain) Normal(stdout);
  520.                         printf("%s\n", (Brackets ? "]" : ""));
  521.                     }
  522.                 } else if (!strcmp(AGArgU(0), "ENDNODE")) {
  523.                     if (InsideNode) {
  524.                         InsideNode = FALSE;
  525.                     }
  526.                 } else if (!strcmp(AGArgU(0), "WORDWRAP")) {
  527.                     PutSetWrap();
  528.                 }    /* End of recognized commands */
  529.                 InC = '\n';
  530.                 Command = TRUE;
  531.             } else if (InC == '{') {                    /* If link or inline command    */
  532.                 ungetc(InC, InFile);
  533.                 AGParseCommand(NULL, InFile);
  534.                 if (AGArgs() >= 3 && !strcmp(AGArgU(1), "LINK")) {    /* If link */
  535.                     WordSend();
  536.                     if (!Plain) {
  537.                         StoreTextMode();
  538.                         Normal(stdout);
  539.                         if (LinkItalics) Italics(stdout);
  540.                         if (LinkBold) Bold(stdout);
  541.                         if (LinkReverse) Reverse(stdout);
  542.                         if (LinkColor != 1) FGColor(stdout, LinkColor);
  543.                     }
  544.                     strncpy(TmpStr, AGArg(0), 60);
  545.                     if (IndexNumbering) {
  546.                         sprintf(TmpStr2, "(%d) %s", NodeNumber(AGArgU(2)), TmpStr);
  547.                         strcpy(TmpStr, TmpStr2);
  548.                     }
  549.                     if (Brackets) {
  550.                         sprintf(TmpStr2, "[%s]", TmpStr);
  551.                         strcpy(TmpStr, TmpStr2);
  552.                     }
  553.                     PutWord(TmpStr);
  554.                     if (!Plain) RestoreTextMode(stdout);
  555.                     InC = '}';
  556.                     Command = TRUE;
  557.                 } else {                                    /* else: Inline command */
  558.                     i = 0;
  559.                     if (!Plain) {
  560.                         if (!strcmp(AGArgU(0), "B")) {
  561.                             WordSend();
  562.                             Bold(stdout);
  563.                         } else if (!strcmp(AGArgU(0), "UB")) {
  564.                             WordSend();
  565.                             BoldOff(stdout);
  566.                         } else if (!strcmp(AGArgU(0), "I")) {
  567.                             WordSend();
  568.                             Italics(stdout);
  569.                         } else if (!strcmp(AGArgU(0), "UI")) {
  570.                             WordSend();
  571.                             ItalicsOff(stdout);
  572.                         } else if (!strcmp(AGArgU(0), "U")) {
  573.                             WordSend();
  574.                             Underline(stdout);
  575.                         } else if (!strcmp(AGArgU(0), "UU")) {
  576.                             WordSend();
  577.                             UnderlineOff(stdout);
  578.                         } else if (!strcmp(AGArgU(0), "FG")) {
  579.                             static int Tmp;
  580.                             WordSend();
  581.                             if (!strcmp(AGArgU(1), "TEXT")) {
  582.                                 Tmp = 1;
  583.                             } else if (!strcmp(AGArgU(1), "HIGHLIGHT")) {
  584.                                 Tmp = ColColor;
  585.                             } else if (!strcmp(AGArgU(1), "BACKGROUND")) {
  586.                                 Tmp = 0;
  587.                             } else {
  588.                                 Tmp = 1;
  589.                             }
  590.                             if (ColColor != 1) FGColor(stdout, Tmp);
  591.                             else if (Tmp != 1 && ColReverse) Reverse(stdout);
  592.                             else ReverseOff(stdout);
  593.                         } else if (!strcmp(AGArgU(0), "BG")) {
  594.                             static int Tmp;
  595.                             WordSend();
  596.                             if (!strcmp(AGArgU(1), "TEXT")) {
  597.                                 Tmp = 1;
  598.                             } else if (!strcmp(AGArgU(1), "HIGHLIGHT")) {
  599.                                 Tmp = ColColor;
  600.                             } else if (!strcmp(AGArgU(1), "BACKGROUND")) {
  601.                                 Tmp = 0;
  602.                             } else {
  603.                                 Tmp = 0;
  604.                             }
  605.                             if (ColColor != 1) BGColor(stdout, Tmp);
  606.                             else if (Tmp != 0 && ColReverse) Reverse(stdout);
  607.                             else ReverseOff(stdout);
  608.                         }
  609.                     }
  610.                     Command = TRUE;
  611.                 }
  612.             } else {        /* There was an @ but no command, so @ must be displayed */
  613.                 ungetc(InC, InFile);
  614.                 InC = '@';
  615.             }
  616.         }
  617.  
  618.         if (!Command && InC != -1 && InsideNode) {        /* If no command    */
  619.             if (InC == '\\') InC = fgetc(InFile);    /* '\' is the escape character */
  620.             if (InC == '\n' || InC == '\t' || InC == ' ') {
  621.                 WordSend();
  622.                 if (InC == '\n') PutEOL();
  623.                 else if (InC == '\t') PutTab();
  624.                 else PutSpace();
  625.             } else {
  626.                 WordAddChar(InC);
  627.             }
  628.         }
  629.  
  630.         LastNewLine = (InC == '\n');
  631.         Command = FALSE;
  632.     }
  633.  
  634.     PutEnd();
  635.     NodeEnd();
  636.     if (!Plain) Normal(stdout);
  637. }
  638.  
  639.  
  640.  
  641. #define MAX_WORD_LENGTH        80
  642.  
  643. static char Word[MAX_WORD_LENGTH+1];
  644. static int WordLen = 0;
  645.  
  646. static void WordAddChar(char NewC) {
  647.     if (WordLen >= MAX_WORD_LENGTH) WordSend();
  648.     Word[WordLen++] = NewC;
  649. }
  650.  
  651. static void WordSend(void) {
  652.     Word[WordLen] = '\000';
  653.     PutWord(Word);
  654.     WordLen = 0;
  655. }
  656.  
  657.  
  658.  
  659. /***********************************\
  660. *                                    *
  661. *    Text displaying put-routines    *
  662. *                                    *
  663. \***********************************/
  664.  
  665. static FILE *PutFile=NULL;
  666. static int PutPos, PutMaxColumn = 77;
  667. static BOOL PutWrap;
  668. static BOOL PutLastSpace;
  669.  
  670. static void PutInit(FILE *OutFile) {
  671.     PutFile = OutFile;
  672.     PutPos = 0;
  673.     PutWrap = FALSE;
  674.     PutLastSpace = FALSE;
  675. }
  676.  
  677. static void PutWord(char *Str) {
  678.     static int Len;
  679.  
  680.     if (PutFile) {
  681. /*        printf("PW: Len=%d, Str=%s\n", strlen(Str), Str);*/
  682.         Len = strlen(Str);
  683.         if (PutWrap) {
  684.             if (PutPos + Len >= PutMaxColumn) {
  685.                 fprintf(PutFile, "\n%s", Str);
  686.                 PutPos = Len - 1;
  687.             } else {
  688.                 if (PutLastSpace) {
  689.                     fputc(' ', PutFile);
  690.                     PutPos++;
  691.                 }
  692.                 fprintf(PutFile, "%s", Str);
  693.                 PutPos += Len;
  694.             }
  695.         } else {
  696.             PutPos += Len;
  697.             if (PutLastSpace) {
  698.                 fputc(' ', PutFile);
  699.                 PutPos++;
  700.             }
  701.             fprintf(PutFile, "%s", Str);
  702.         }
  703.         PutLastSpace = FALSE;
  704.     }
  705. }
  706.  
  707. static void PutEOL(void) {
  708.     if (PutFile) {
  709.         fputc('\n', PutFile);
  710.         PutPos = 0;
  711.         PutLastSpace = FALSE;
  712.     }
  713. }
  714.  
  715. static void PutTab(void) {
  716.     if (PutFile) {
  717.         PutPurgeSpace();
  718.         fputc('\t', PutFile);
  719.         PutPos = (PutPos + 8) & (~7);
  720.     }
  721. }
  722.  
  723. static void PutSpace(void) {
  724.     if (PutFile) {
  725.         if (PutWrap) {
  726.             if (PutLastSpace) {
  727.                 PutLastSpace = FALSE;
  728.                 PutWord(" ");
  729.             }
  730.             PutLastSpace = TRUE;
  731.         } else {
  732.             PutWord(" ");
  733.         }
  734.     }
  735. }
  736.  
  737. static void PutPurgeSpace(void) {
  738.     if (PutFile) {
  739.         if (PutLastSpace) {
  740.             if (PutPos + 1 >= PutMaxColumn) {
  741.                 fputc('\n', PutFile);
  742.                 PutPos = 0;
  743.             } else {
  744.                 fputc(' ', PutFile);
  745.                 PutPos++;
  746.             }
  747.         }
  748.         PutLastSpace = FALSE;
  749.     }
  750. }
  751.  
  752. static void PutSetWidth(int Width) {
  753.     if (Width < 20) Width = 20;
  754.  
  755.     PutMaxColumn = Width - 1;
  756. }
  757.  
  758. static void PutSetWrap(void) {
  759.     PutWrap = TRUE;
  760. }
  761.  
  762. static void PutEnd(void) {
  763.     PutFile = NULL;
  764. }
  765.  
  766.  
  767. struct NodeNode {
  768.     struct NodeNode *Left;
  769.     struct NodeNode *Right;
  770.     long Index;
  771.     char *Text;
  772. };
  773.  
  774.  
  775.  
  776.  
  777. /***********************************\
  778. *                                    *
  779. *    Node handling routines            *
  780. *                                    *
  781. \***********************************/
  782.  
  783. static int NodeAmount = -1;
  784. static struct NodeNode *NodeNode=NULL;
  785.  
  786.  
  787. /* The following function is only for internal use    */
  788. /*        -> no prototype provided                    */
  789. static struct NodeNode *AddNode(char *Str) {
  790.     static struct NodeNode *NewNode;
  791.  
  792.     NewNode = malloc(sizeof(struct NodeNode) + strlen(Str) + 1);
  793.     if (NewNode) {
  794.         NewNode -> Left = NULL;
  795.         NewNode -> Right = NULL;
  796.         NewNode -> Index = ++NodeAmount;
  797.         NewNode -> Text = ((char *) NewNode) + sizeof(struct NodeNode);
  798.         strcpy(NewNode -> Text, Str);
  799.     } else {
  800.         fprintf(stderr,"   *** PANIC: Couldn't allocate memory!\n");
  801.         exit(EXIT_FAILURE);
  802.     }
  803.  
  804.     return NewNode;
  805. }
  806.  
  807.  
  808.  
  809. /* The following function is only for internal use    */
  810. /*        -> no prototype provided                    */
  811. static int FindNode(struct NodeNode *CurrNode, char *Str) {
  812.     int Tmp;
  813.  
  814.     if (CurrNode == NULL) {
  815.         NodeNode = AddNode(Str);
  816.         return 1;
  817.     } else {
  818.         Tmp = strcmp(Str, CurrNode->Text);
  819.         if (Tmp < 0) {
  820.             if (CurrNode->Left) {
  821.                 return FindNode(CurrNode->Left, Str);
  822.             } else {
  823.                 CurrNode -> Left = AddNode(Str);
  824.                 return CurrNode -> Left -> Index;
  825.             }
  826.         } else if (Tmp>0) {
  827.             if (CurrNode->Right) {
  828.                 return FindNode(CurrNode->Right, Str);
  829.             } else {
  830.                 CurrNode -> Right = AddNode(Str);
  831.                 return CurrNode -> Right -> Index;
  832.             }
  833.         } else {
  834.             return CurrNode -> Index;
  835.         }
  836.     }
  837. }
  838.  
  839.  
  840.  
  841.  
  842. static int NodeNumber(char *Str) {
  843.     if (NodeAmount >= 0) return FindNode(NodeNode, Str);
  844.  
  845.     return -1;
  846. }
  847.  
  848.  
  849.  
  850.  
  851. static void NodeInit(void) {
  852.     NodeEnd();
  853.     NodeAmount = 0;
  854. }
  855.  
  856.  
  857.  
  858. /* The following function is only for internal use    */
  859. /*        -> no prototype provided                    */
  860. static void FreeNodes(struct NodeNode *CurrNode) {
  861.     if (CurrNode -> Left) FreeNodes(CurrNode->Left);
  862.     if (CurrNode -> Right) FreeNodes(CurrNode->Right);
  863.     free(CurrNode);
  864. }
  865.  
  866.  
  867.  
  868. static void NodeEnd(void) {
  869.     if (NodeNode && NodeAmount >= 0) FreeNodes(NodeNode);
  870.     NodeNode = NULL;
  871.     NodeAmount = -1;
  872. }
  873.  
  874.  
  875.  
  876.  
  877. /***********************************\
  878. *                                    *
  879. *    AmigaGuide parsing routines        *
  880. *                                    *
  881. \***********************************/
  882.  
  883. #define AG_MAX_CHARS_PER_ARGUMENT        80
  884. #define AG_MAX_ARGUMENTS_PER_COMMAND    4
  885.  
  886. static char AGOriginalArgument[AG_MAX_ARGUMENTS_PER_COMMAND][AG_MAX_CHARS_PER_ARGUMENT+1];
  887. static char AGUppercaseArgument[AG_MAX_ARGUMENTS_PER_COMMAND][AG_MAX_CHARS_PER_ARGUMENT+1];
  888. static int AGArguments;
  889. static char AGNULL = '\000';
  890.  
  891. /*-----------------------------------------------------------------*\
  892. |                                                                    |
  893. | AGParseCommand - read AmigaGuide command from a string or file    |
  894. |    and parse it.                                                    |
  895. |                                                                    |
  896. | Synopsis:                                                            |
  897. |    int Args = AGParseCommand(char *Command, NULL);                    |
  898. |    int Args = AGParseCommand(NULL, FILE *InFile);                    |
  899. |                                                                    |
  900. | Function:                                                            |
  901. |    Reads an AmigaGuide command either from a string or a file and    |
  902. |    parses it. AGParseCommand assumes that leading @ is not in the    |
  903. |    string / has been read from the file. If the first character is    |
  904. |    "{" the command is assumed to be an inline command, otherwise    |
  905. |    it is assumed to be a full command line.                        |
  906. |                                                                    |
  907. | Inputs:                                                            |
  908. |    char *Command - if not NULL, this is taken to be the Command    |
  909. |    FILE *InFile - if not NULL, this is the file where the Command    |
  910. |        will be read.                                                |
  911. |    Command and InFile are mutually exclusive.                        |
  912. |                                                                    |
  913. | Result:                                                            |
  914. |    Args - The amount of arguments read from the command            |
  915. |                                                                    |
  916. \*-----------------------------------------------------------------*/
  917.  
  918. static int AGParseCommand(char *Command, FILE *InFile) {
  919.     int CPos = -1, ArgPos = 0;
  920.     BOOL InQuotes = FALSE, GoQuotes = FALSE, CommandLine = FALSE, Quit = FALSE;
  921.     static int Curr;
  922.  
  923.     AGArguments = 0;
  924.  
  925.     Curr = fgetc(InFile);
  926.     if (Curr != '{') {
  927.         CommandLine = TRUE;
  928.         ungetc(Curr, InFile);
  929.     }
  930.  
  931.     do {
  932.         if (!InFile) Curr = (unsigned char) Command[++CPos];
  933.         else Curr = fgetc(InFile);
  934.  
  935.         if (!Curr || Curr == -1 || Curr == '\n' ||
  936.                     !CommandLine && !InQuotes && Curr == '}') {
  937.             Curr = ' ';
  938.             Quit = TRUE;
  939.         }
  940.  
  941.         if (Curr == '"') {
  942.             Curr = ' ';
  943.             if (!InQuotes) GoQuotes = TRUE;
  944.             else InQuotes = FALSE;
  945.         }
  946.  
  947.         if(IsSpace(Curr) && ArgPos && !InQuotes) {
  948.             if (AGArguments <= AG_MAX_ARGUMENTS_PER_COMMAND) {
  949.                 AGOriginalArgument[AGArguments-1][ArgPos] = '\000';
  950.                 AGUppercaseArgument[AGArguments-1][ArgPos] = '\000';        
  951.             }
  952.             ArgPos = 0;
  953.         } else {
  954.             if (ArgPos < AG_MAX_CHARS_PER_ARGUMENT && (!IsSpace(Curr) || InQuotes)) {
  955.                 if (!ArgPos) AGArguments++;
  956.                 if (AGArguments <= AG_MAX_ARGUMENTS_PER_COMMAND) {
  957.                     AGOriginalArgument[AGArguments-1][ArgPos] = Curr;
  958.                     AGUppercaseArgument[AGArguments-1][ArgPos] = ToUpper(Curr);
  959.                 }
  960.                 ArgPos++;
  961.             }
  962.         }
  963.  
  964.         if (GoQuotes) {
  965.             InQuotes = TRUE;
  966.             GoQuotes = FALSE;
  967.         }
  968.     } while (!Quit);
  969.  
  970.     if (AGArguments > AG_MAX_ARGUMENTS_PER_COMMAND)
  971.         AGArguments = AG_MAX_ARGUMENTS_PER_COMMAND;
  972.  
  973.     return AGArguments;
  974. }
  975.  
  976.  
  977. /*-----------------------------------------------------------------*\
  978. |                                                                    |
  979. | AGArgs - returns last AmigaGuide command argument count            |
  980. |                                                                    |
  981. | Synopsis:                                                            |
  982. |    int Args = AGArgs();                                            |
  983. |                                                                    |
  984. | Function:                                                            |
  985. |    Returns argument count from last call of AGParseCommand()        |
  986. |                                                                    |
  987. | Inputs:                                                            |
  988. |                                                                    |
  989. | Result:                                                            |
  990. |    Args - The amount of arguments read from the command            |
  991. |                                                                    |
  992. \*-----------------------------------------------------------------*/
  993.  
  994. static int AGArgs(void) {
  995.     return AGArguments;
  996. }
  997.  
  998.  
  999. /*-----------------------------------------------------------------*\
  1000. |                                                                    |
  1001. | AGArg - returns desired argument from last AmigaGuide command        |
  1002. |                                                                    |
  1003. | Synopsis:                                                            |
  1004. |    char *Arg = AGArg(int ArgNo);                                    |
  1005. |                                                                    |
  1006. | Function:                                                            |
  1007. |    Returns desired argument from last call of AGParseCommand().    |
  1008. |                                                                    |
  1009. | Inputs:                                                            |
  1010. |    ArgNo - 0 .. AGArgs()-1                                            |
  1011. |                                                                    |
  1012. | Result:                                                            |
  1013. |    Arg - A pointer to the desired argument. If ArgNo was out of    |
  1014. |        range, a pointer to an empty string is returned.            |
  1015. |                                                                    |
  1016. \*-----------------------------------------------------------------*/
  1017.  
  1018. static char *AGArg(int Arg) {
  1019.     if (Arg <= AG_MAX_ARGUMENTS_PER_COMMAND && Arg >= 0) return AGOriginalArgument[Arg];
  1020.     else return &AGNULL;
  1021. }
  1022.  
  1023. /*-----------------------------------------------------------------*\
  1024. |                                                                    |
  1025. | AGArg - returns desired argument from last AmigaGuide command        |
  1026. |    converted to uppercase.                                            |
  1027. |                                                                    |
  1028. | Synopsis:                                                            |
  1029. |    char *Arg = AGArg(int ArgNo);                                    |
  1030. |                                                                    |
  1031. | Function:                                                            |
  1032. |    Returns desired argument from last call of AGParseCommand()        |
  1033. |    converted to uppercase letters.                                    |
  1034. |                                                                    |
  1035. | Inputs:                                                            |
  1036. |    ArgNo - 0 .. AGArgs()-1                                            |
  1037. |                                                                    |
  1038. | Result:                                                            |
  1039. |    Arg - A pointer to the desired argument. If ArgNo was out of    |
  1040. |        range, a pointer to an empty string is returned.            |
  1041. |                                                                    |
  1042. \*-----------------------------------------------------------------*/
  1043.  
  1044. static char *AGArgU(int Arg) {
  1045.     if (Arg <= AG_MAX_ARGUMENTS_PER_COMMAND && Arg >= 0) return AGUppercaseArgument[Arg];
  1046.     else return &AGNULL;
  1047. }
  1048.